package com.sloan.community.filter;

import com.sloan.community.common.Login;
import com.sloan.community.common.res.Result;
import com.sloan.community.po.User;
import com.sloan.community.util.JsonUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

import static com.sloan.community.common.Constant.ERROR_MSG;
import static com.sloan.community.common.Constant.SESSION_USER;

@Slf4j
@Component
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
public class AnnotationLoginWebFilter implements WebFilter, PriorityOrdered {

    private final RequestMappingHandlerMapping requestMappingHandlerMapping;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        log.info("进入过滤器URL: {}", exchange.getRequest().getURI().getPath());
        return requestMappingHandlerMapping.getHandler(exchange)
                // 静态资源放行,静态资源不走过滤器,
                // (这里requestMappingHandlerMapping获取的是AbstractHandlerMethodMapping，而WebSocket获取的是AbstractUrlHandlerMapping)
                .switchIfEmpty(chain.filter(exchange))
                .flatMap(handler -> {
                    // 这里是为了前端图片上传用的,图片上传不是HandlerMethod类型
                    if (!(handler instanceof HandlerMethod handlerMethod)) {
                        return chain.filter(exchange);
                    }

                    Login login = handlerMethod.getMethodAnnotation(Login.class);
                    if (Objects.isNull(login) || !login.required()) {
                        return chain.filter(exchange);
                    }

                    // 需要认证
                    return exchange.getSession()
                            .flatMap(session -> {
                                User user = session.getAttribute(SESSION_USER);
                                if (Objects.nonNull(user)) {
                                    return chain.filter(exchange);
                                }

                                return this.toLogin(exchange, handlerMethod);
                            });
                }).doOnTerminate(() -> {
                    // 每次请求结束后清理
                    exchange.getAttributes().remove(ERROR_MSG);
                });
    }

    public Mono<Void> toLogin(ServerWebExchange exchange, HandlerMethod handler) {
        // 当前的 @RestController,以及继承的类加有@RestController，或者自身标注的类加有@RestController都可以获取到
        boolean methodHasAnno = AnnotatedElementUtils.hasAnnotation(
                handler.getReturnType().getAnnotatedElement(), ResponseBody.class);
        boolean classHasAnno = AnnotatedElementUtils.hasAnnotation(handler.getClass(), ResponseBody.class);

        boolean isJson = exchange.getRequest().getHeaders()
                .getAccept().contains(MediaType.APPLICATION_JSON);

        if (methodHasAnno || classHasAnno || isJson) {
            return this.responseNoLogin(exchange);
        }
//        return this.forwardLoginPage(exchange, chain);
        return this.redirectLoginPage(exchange);
    }

    private Mono<Void> responseNoLogin(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
//        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        String json = JsonUtils.serialize(Result.access());
        return response.writeWith(Mono.just(
                response.bufferFactory().wrap(json.getBytes(StandardCharsets.UTF_8))));
    }


    public Mono<Void> forwardLoginPage(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest()
                .mutate()
                .method(HttpMethod.GET)
                .path("/login.html") // TODO 等待优化，看能不能放到请求域中
                .header("msg", "用户信息失效，请重新登录！")
                .build();

        ServerWebExchange newExchange = exchange.mutate().request(request).build();
        return chain.filter(newExchange);
    }

    public Mono<Void> redirectLoginPage(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.FOUND);
        response.getHeaders().setLocation(URI.create("/login.html"));
        return response.setComplete();
    }

    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE;
    }
}
